package lexer;

import utils.CompileError;

import java.util.ArrayList;

/**
 * 词法分析程序
 */
public class Tokenizer {
    public ArrayList<Token> tokens;
    public int idx;
    private int line=1;
    private Reader reader;

    public Tokenizer(Reader reader) {
        this.reader = reader;
        tokens = new ArrayList<>();
        idx = 0;
        Token token = null;
        do {
            try {
                tokens.add(nextToken());
                token = tokens.get(idx++);
            } catch (CompileError e) {
                e.printStackTrace();
                System.exit(1);//词法分析错误
            }
        } while (token.type() != TokenType.EOF);
        idx = -1;
    }

    public void printAllToken() {
        for (Token token :
                tokens) {
            System.out.println(token);
        }
    }

    public Token backToLastToken() {
        idx--;
        if (idx >= 0)
            return tokens.get(idx);
        return null;
    }

    public Token getNextToken() {
        idx++;
        if (idx == tokens.size()) {
            return null;
        }
        return tokens.get(idx);
    }

    private Token nextToken() throws CompileError {
        skipSpace();//此时nowCh不是空白或者读到了文件末尾
        if (reader.isEOF()) {//读到文件末尾
            return new Token(TokenType.EOF, "",line);
        }
        //没到末尾，nowCh不是空白
        char now = reader.nowCh;
        if (Character.isDigit(now)) { // 数字开头，是数字
            return numberToken();
        } else if (Character.isLetter(now) || now == '_') {// 字母或下划线开头，是字母或下划线
            return identOrKeywordToken();
        } else //开头不是数字、字母、下划线
            return signToken();
    }

    private Token numberToken() throws CompileError {
        char one = reader.nowCh;
        char two = reader.nextChar();
        if (one == '0') {
            if (two == 'x' || two == 'X') {//十六进制
                reader.getChar();//读取x
                char next = reader.nextChar();
                StringBuilder tmp = new StringBuilder();
                while (next != 0 && (Character.isDigit(next) || ((next <= 'F' && next >= 'A') || (next <= 'f' && next >= 'a')))) {
                    tmp.append(reader.getChar());
                    next = reader.nextChar();
                }
                int num = Integer.parseInt(tmp.toString(), 16);
                return new Token(TokenType.Number, num,line);
            } else {//八进制
                StringBuilder tmp = new StringBuilder("0");
                char next = reader.nextChar();
                while (next <= '7' && next >= '0') {
                    tmp.append(reader.getChar());
                    next = reader.nextChar();
                }
                int num = Integer.parseInt(tmp.toString(), 8);
                return new Token(TokenType.Number, num,line);
            }
        } else {//十进制
            StringBuilder tmp = new StringBuilder(Character.toString(reader.nowCh));
            while (Character.isDigit(reader.nextChar())) {
                tmp.append(reader.getChar());
            }
            int num = Integer.parseInt(tmp.toString());
            return new Token(TokenType.Number, num,line);
        }
    }

    private Token identOrKeywordToken() throws CompileError {
        StringBuilder tmp = new StringBuilder(Character.toString(reader.nowCh));
        while (Character.isDigit(reader.nextChar()) || Character.isLetter(reader.nextChar()) || reader.nextChar() == '_') {
            tmp.append(reader.getChar());
        }
        String value = tmp.toString();
        switch (value) {
            case "if":
                return new Token(TokenType.If, "if",line);
            case "else":
                return new Token(TokenType.Else, "else",line);
            case "while":
                return new Token(TokenType.While, "while",line);
            case "break":
                return new Token(TokenType.Break, "break",line);
            case "continue":
                return new Token(TokenType.Continue, "continue",line);
            case "return":
                return new Token(TokenType.Return, "return",line);
            case "const":
                return new Token(TokenType.Const, "const",line);
            case "int":
                return new Token(TokenType.Int, "int",line);
            case "void":
                return new Token(TokenType.Void, "void",line);
            default:
                return new Token(TokenType.Ident, value,line);

        }

    }

    private Token signToken() throws CompileError {
        char one = reader.nowCh;
        char two = reader.nextChar();
        if (one == '=') {
            if (two == '=') {
                reader.getChar();
                return new Token(TokenType.Eq, "==",line);
            }
            return new Token(TokenType.Assign, "=",line);
        } else if (one == ';') {
            return new Token(TokenType.Semicolon, ";",line);
        } else if (one == ',') {
            return new Token(TokenType.Comma, ",",line);
        } else if (one == '(') {
            return new Token(TokenType.LPar, "(",line);
        } else if (one == ')') {
            return new Token(TokenType.RPar, ")",line);
        } else if (one == '{') {
            return new Token(TokenType.LBrace, "{",line);
        } else if (one == '}') {
            return new Token(TokenType.RBrace, "}",line);
        } else if (one == '[') {
            return new Token(TokenType.LSquare, "[",line);
        } else if (one == ']') {
            return new Token(TokenType.RSquare, "]",line);
        } else if (one == '+' || one == '-') {
            int numMinus = 0;
            if (one == '-') {
                numMinus += 1;
            }
            while (two == '+' || two == '-' || two == ' ') {
                reader.getChar();
                if (two == '-') {
                    numMinus += 1;
                }
                two = reader.nextChar();
            }
            if (numMinus % 2 == 0) {
                return new Token(TokenType.Plus, "+",line);
            } else {
                return new Token(TokenType.Minus, "-",line);
            }
        } else if (one == '*') {
            return new Token(TokenType.Mult, "*",line);
        } else if (one == '/') {
            if (two == '/') {
                while (reader.getChar() != '\n') ;
                return nextToken();
            } else if (two == '*') {
                reader.getChar();//读到*
                while (!(reader.getChar() == '*' && reader.nextChar() == '/')) {
                    if (reader.isEOF()) {
                        return new Token(TokenType.EOF, "",line);
                    }
                }
                reader.getChar();//读到/
                return nextToken();
            } else {
                return new Token(TokenType.Div, "/",line);
            }
        } else if (one == '%') {
            return new Token(TokenType.Modulo, "%",line);
        } else if (one == '<') {
            if (two == '=') {
                reader.getChar();
                return new Token(TokenType.Lte, "<=",line);
            } else {
                return new Token(TokenType.Lt, "<",line);
            }
        } else if (one == '>') {
            if (two == '=') {
                reader.getChar();
                return new Token(TokenType.Gte, ">=",line);
            } else {
                return new Token(TokenType.Gt, ">",line);
            }
        } else if (one == '!') {
            if (two == '=') {
                reader.getChar();
                return new Token(TokenType.NEq, "!=",line);
            } else {
                int numNot = 1;
                while (two == '!' || two == ' ') { // 处理！！！！！得情况
                    reader.getChar();
                    two = reader.nextChar();
                    if (reader.nowCh == ' ')
                        continue;
                    if (two != '=') {
                        numNot++;
                    }
                }
                if (numNot % 2 == 1)
                    return new Token(TokenType.Not, "!",line);
                else // 偶数个就是没有
                    return nextToken();
            }
        } else if (one == '&' && two == '&') {
            reader.getChar();
            return new Token(TokenType.And, "&&",line);
        } else if (one == '|' && two == '|') {
            reader.getChar();
            return new Token(TokenType.Or, "||",line);
        } else {//出错！！！！
            throw new CompileError(reader.pos + ":非法token");
        }
    }

    private void skipSpace() {
        if (!reader.isEOF()) {
            do {
                reader.getChar();//注意这里实现的向下读取一个字符！！！
                if(reader.nowCh=='\n'){
                    line++;
                }
            } while (!reader.isEOF() && Character.isWhitespace(reader.nowCh));
        }
    }

}
